﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Yxinmt.SmarterStreaming
{
    public delegate void SetEventCallBack(UInt32 event_id, Int64 param1, Int64 param2, UInt64 param3, [MarshalAs(UnmanagedType.LPStr)] String param4, [MarshalAs(UnmanagedType.LPStr)] String param5, IntPtr param6);

    public class SmartPlayer
    {
        SmartPlayerConfig _Config { get; set; }
        System.Windows.Controls.Image _Image { get; set; }
        System.Windows.Forms.PictureBox _PictureBox { get; set; }
        bool _IsPlaying { get; set; }
        IntPtr _PlayerHandle { get; set; }
        System.Windows.Window _Window { get; set; }

        static SP_SDKEventCallBack _SDKEventCallBack { get; set; }

        static ConcurrentDictionary<IntPtr, SetEventCallBack> _SetEventCallBackDictionary { get; set; }


        static SmartPlayer()
        {
            _SDKEventCallBack = new SP_SDKEventCallBack(SDKEventCallBack);
            _SetEventCallBackDictionary = new ConcurrentDictionary<IntPtr, SetEventCallBack>();
        }

        ~SmartPlayer()
        {
            RemoveHandle();
        }

        void RemoveHandle()
        {
            try
            {
                SetEventCallBack setEventCallBack = null;
                if (_PlayerHandle != IntPtr.Zero && _SetEventCallBackDictionary.ContainsKey(_PlayerHandle)) _SetEventCallBackDictionary.TryRemove(_PlayerHandle, out setEventCallBack);
            }
            catch
            {

            }
        }

        public SmartPlayer(System.Windows.Window window, System.Windows.Controls.Image image, SmartPlayerConfig config, Action<string> setStatus)
        {
            _Image = image;
            _Config = SmartPlayerConfig.Clone(config);
            _Window = window;
            _SetStatus = setStatus;

            _StatusMessageList = new ConcurrentQueue<string>();
            _StatusMessageList.Enqueue(config.PlayUrl);

            InitLoad();
        }

        public SmartPlayer(System.Windows.Window window, System.Windows.Forms.PictureBox pictureBox, SmartPlayerConfig config, Action<string> setStatus)
        {
            _PictureBox = pictureBox;
            _Config = SmartPlayerConfig.Clone(config);
            _Window = window;
            _SetStatus = setStatus;

            _StatusMessageList = new ConcurrentQueue<string>();
            _StatusMessageList.Enqueue(config.PlayUrl);

            InitLoad();
        }

        public SmartPlayer(System.Windows.Window window, SmartPlayerConfig config)
        {
            _Config = SmartPlayerConfig.Clone(config);
            _Window = window;

            _StatusMessageList = new ConcurrentQueue<string>();
            _StatusMessageList.Enqueue(config.PlayUrl);

            InitLoad();
        }
        static bool _IsInit { get; set; }

        public static void InitSDK()
        {
            UInt32 isInited = NTSmartPlayerSDK.NT_SP_Init(0, IntPtr.Zero);
            if (isInited != 0) throw new Exception("初始化流媒体播放失败！");
            _IsInit = true;
        }

        public static void UnInitSDK()
        {
            NTSmartPlayerSDK.NT_SP_UnInit();
            _IsInit = false;
        }

        IntPtr _PictureBoxIntPtr { get; set; }

        void InitLoad()
        {
            if (!_IsInit) throw new Exception("初始化流媒体播放失败，请退出启动程序！");

            //if (_Image != null && _Image.ActualWidth == 0) return;
            //else if (_PictureBox != null && _PictureBox.Width == 0) return;

            if (_Image != null) _PictureBoxIntPtr = ((System.Windows.Interop.HwndSource)System.Windows.PresentationSource.FromVisual(_Image)).Handle;
            else if (_PictureBox != null) _PictureBoxIntPtr = _PictureBox.Handle;

            if (_PlayerHandle == IntPtr.Zero)
            {
                IntPtr playerHandle = IntPtr.Zero;
                UInt32 retOpen = NTSmartPlayerSDK.NT_SP_Open(out playerHandle, _PictureBoxIntPtr, 0, IntPtr.Zero);
                _PlayerHandle = playerHandle;

                if (retOpen != 0)
                {
                    _PlayerHandle = IntPtr.Zero;
                    throw new Exception("调用NT_SP_Open失败..");
                }

                if (_Image != null) NTSmartPlayerSDK.NT_SP_OnWindowSize(_PlayerHandle, Convert.ToInt32(_Image.ActualWidth), Convert.ToInt32(_Image.ActualHeight));
                else if (_PictureBox != null) NTSmartPlayerSDK.NT_SP_OnWindowSize(_PlayerHandle, _PictureBox.Width, _PictureBox.Height);
            }

            if (_PlayerHandle != IntPtr.Zero)
            {
                _SetEventCallBackDictionary.AddOrUpdate(_PlayerHandle, new SetEventCallBack(EventCallBack), (key, value) => value);
                NTSmartPlayerSDK.NT_SP_SetEventCallBack(_PlayerHandle, _PictureBoxIntPtr, _SDKEventCallBack);
            }
        }

        static void SDKEventCallBack(IntPtr handle, IntPtr user_data, UInt32 event_id, Int64 param1, Int64 param2, UInt64 param3, [MarshalAs(UnmanagedType.LPStr)] String param4, [MarshalAs(UnmanagedType.LPStr)] String param5, IntPtr param6)
        {
            if (_SetEventCallBackDictionary.ContainsKey(handle)) _SetEventCallBackDictionary[handle](event_id, param1, param2, param3, param4, param5, param6);
        }

        void EventCallBack(UInt32 event_id, Int64 param1, Int64 param2, UInt64 param3, [MarshalAs(UnmanagedType.LPStr)] String param4, [MarshalAs(UnmanagedType.LPStr)] String param5, IntPtr param6)
        {
            if (!_IsPlaying) return;

            if ((UInt32)NTSmartPlayerDefine.NT_SP_E_EVENT_ID.NT_SP_E_EVENT_ID_PLAYBACK_REACH_EOS == event_id)
            {
                Stop();
                return;
            }
            else if ((UInt32)NTSmartPlayerDefine.NT_SP_E_EVENT_ID.NT_SP_E_EVENT_ID_RECORDER_REACH_EOS == event_id)
            {
                return;
            }
            else if ((UInt32)NTSmartPlayerDefine.NT_SP_E_EVENT_ID.NT_SP_E_EVENT_ID_PULLSTREAM_REACH_EOS == event_id)
            {
                if (_PlayerHandle != IntPtr.Zero)
                {
                    NTSmartPlayerSDK.NT_SP_StopPullStream(_PlayerHandle);
                }

                return;
            }

            uint connectionStatus = 0;
            int bufferStatus = 0;
            int downloadSpeed = 0;

            if ((UInt32)NTSmartPlayerDefine.NT_SP_E_EVENT_ID.NT_SP_E_EVENT_ID_CONNECTING == event_id
                || (UInt32)NTSmartPlayerDefine.NT_SP_E_EVENT_ID.NT_SP_E_EVENT_ID_CONNECTION_FAILED == event_id
                || (UInt32)NTSmartPlayerDefine.NT_SP_E_EVENT_ID.NT_SP_E_EVENT_ID_CONNECTED == event_id
                || (UInt32)NTSmartPlayerDefine.NT_SP_E_EVENT_ID.NT_SP_E_EVENT_ID_DISCONNECTED == event_id)
            {
                connectionStatus = event_id;
            }

            if ((UInt32)NTSmartPlayerDefine.NT_SP_E_EVENT_ID.NT_SP_E_EVENT_ID_START_BUFFERING == event_id
                || (UInt32)NTSmartPlayerDefine.NT_SP_E_EVENT_ID.NT_SP_E_EVENT_ID_BUFFERING == event_id
                || (UInt32)NTSmartPlayerDefine.NT_SP_E_EVENT_ID.NT_SP_E_EVENT_ID_STOP_BUFFERING == event_id)
            {
                bufferStatus = (int)event_id;

                if ((UInt32)NTSmartPlayerDefine.NT_SP_E_EVENT_ID.NT_SP_E_EVENT_ID_BUFFERING == event_id)
                {
                    bufferStatus = (int)param1;
                }
            }

            if ((UInt32)NTSmartPlayerDefine.NT_SP_E_EVENT_ID.NT_SP_E_EVENT_ID_DOWNLOAD_SPEED == event_id)
            {
                downloadSpeed = (int)param1;
            }

            string showStr = "";

            if (connectionStatus != 0)
            {
                showStr += "链接状态: ";

                if ((UInt32)NTSmartPlayerDefine.NT_SP_E_EVENT_ID.NT_SP_E_EVENT_ID_CONNECTING == connectionStatus) showStr += "链接中";
                else if ((UInt32)NTSmartPlayerDefine.NT_SP_E_EVENT_ID.NT_SP_E_EVENT_ID_CONNECTION_FAILED == connectionStatus) showStr += "链接失败";
                else if ((UInt32)NTSmartPlayerDefine.NT_SP_E_EVENT_ID.NT_SP_E_EVENT_ID_CONNECTED == connectionStatus) showStr += "链接成功";
                else if ((UInt32)NTSmartPlayerDefine.NT_SP_E_EVENT_ID.NT_SP_E_EVENT_ID_DISCONNECTED == connectionStatus) showStr += "链接断开";
            }

            if (downloadSpeed != -1)
            {
                String ss = "  下载速度: " + (downloadSpeed * 8 / 1000).ToString() + "kbps " + (downloadSpeed / 1024).ToString() + "KB/s";

                showStr += ss;
            }

            if (bufferStatus != 0)
            {
                showStr += "  缓冲状态: ";

                if ((UInt32)NTSmartPlayerDefine.NT_SP_E_EVENT_ID.NT_SP_E_EVENT_ID_START_BUFFERING == bufferStatus) showStr += "开始缓冲";
                else if ((UInt32)NTSmartPlayerDefine.NT_SP_E_EVENT_ID.NT_SP_E_EVENT_ID_BUFFERING == bufferStatus) { string ss = "缓冲中 " + bufferStatus.ToString() + "%"; showStr += ss; }
                else if ((UInt32)NTSmartPlayerDefine.NT_SP_E_EVENT_ID.NT_SP_E_EVENT_ID_STOP_BUFFERING == bufferStatus) showStr += "结束缓冲";
            }

            if (_SetStatus != null) _SetStatus(showStr);

#if DEBUG
            Console.WriteLine(showStr);
#else
            if (_Config.IsLog) _StatusMessageList.Enqueue(showStr);
#endif
        }

        Action<string> _SetStatus { get; set; }

        ConcurrentQueue<string> _StatusMessageList { get; set; }

        void WriteStatusLog()
        {
            if (!_Config.IsLog) return;

            Utility.LoggerProxy.Info("SmartPlayer", "WriteStatusLog", _StatusMessageList.ToList());

            _StatusMessageList = new ConcurrentQueue<string>();
        }

        SP_SDKVideoSizeCallBack _SP_SDKVideoSizeCallBack { get; set; }


        /// <summary>
        /// 播放
        /// </summary>
        public void Play()
        {
            if (!InitCommonSDKParam()) throw new Exception("设置参数错误!");

            _SP_SDKVideoSizeCallBack = new SP_SDKVideoSizeCallBack(SP_SDKVideoSizeHandle);
            NTSmartPlayerSDK.NT_SP_SetVideoSizeCallBack(_PlayerHandle, _PictureBoxIntPtr, _SP_SDKVideoSizeCallBack);

            UInt32 retStart = NTSmartPlayerSDK.NT_SP_StartPlay(_PlayerHandle);

            if (retStart != 0) throw new Exception("播放失败..");

            _IsPlaying = true;
        }

        void SP_SDKVideoSizeHandle(IntPtr handle, IntPtr userData, Int32 width, Int32 height)
        {
            if (_Image != null) NTSmartPlayerSDK.NT_SP_OnWindowSize(handle, Convert.ToInt32(_Image.ActualWidth), Convert.ToInt32(_Image.ActualHeight));
            else if (_PictureBox != null) NTSmartPlayerSDK.NT_SP_OnWindowSize(handle, _PictureBox.Width, _PictureBox.Height);
        }

        public void SetSize(Int32 width, Int32 height)
        {
            NTSmartPlayerSDK.NT_SP_OnWindowSize(_PlayerHandle, width, height);
        }

        int _RotateDegrees { get; set; }

        bool InitCommonSDKParam()
        {
            if (_IsPlaying) return true;

            if (IntPtr.Zero == _PlayerHandle) return false;

            if (string.IsNullOrEmpty(_Config.PlayUrl)) return false;

            NTSmartPlayerSDK.NT_SP_SetBuffer(_PlayerHandle, _Config.BufferTime);

            // 设置rtsp tcp模式，rtmp不使用, 可以不设置
            NTSmartPlayerSDK.NT_SP_SetRTSPTcpMode(_PlayerHandle, _Config.IsRtspTcp ? 1 : 0);

            NTSmartPlayerSDK.NT_SP_SetMute(_PlayerHandle, _Config.IsMute ? 1 : 0);

            NTSmartPlayerSDK.NT_SP_SetFastStartup(_PlayerHandle, _Config.IsFastStartup ? 1 : 0);

            // 设置低延迟模式
            NTSmartPlayerSDK.NT_SP_SetLowLatencyMode(_PlayerHandle, _Config.IsLowLatency ? 1 : 0);

            NTSmartPlayerSDK.NT_SP_SetRotation(_PlayerHandle, _RotateDegrees);

            NTSmartPlayerSDK.NT_SP_SetReportDownloadSpeed(_PlayerHandle, 1, 1);

            NTSmartPlayerSDK.NT_SP_SetURL(_PlayerHandle, _Config.PlayUrl);

            return true;
        }

        /// <summary>
        /// 停止
        /// </summary>
        public void Stop()
        {
            if (_PlayerHandle == IntPtr.Zero) return;

            if (_IsPlaying) NTSmartPlayerSDK.NT_SP_StopPlay(_PlayerHandle);

            if (_PlayerHandle != IntPtr.Zero) NTSmartPlayerSDK.NT_SP_Close(_PlayerHandle);

            WriteStatusLog();
        }

        public void StartRecord(string dir, string fileName, string playUrl)
        {
            _Config.LocalRecorder.RecordDir = dir;
            _Config.LocalRecorder.FileNamePrefix = fileName;
            _Config.PlayUrl = playUrl;

            if (!InitCommonSDKParam()) throw new Exception("设置参数错误!");
           
            byte[] buffer1 = Encoding.Default.GetBytes(_Config.LocalRecorder.RecordDir);
            byte[] buffer2 = Encoding.Convert(Encoding.UTF8, Encoding.Default, buffer1, 0, buffer1.Length);
            string recDir = Encoding.Default.GetString(buffer2, 0, buffer2.Length);

            if (string.IsNullOrEmpty(recDir)) throw new Exception("请设置录像目录");

            if (_PlayerHandle == IntPtr.Zero) return;

            uint ret = NTSmartPlayerSDK.NT_SP_SetRecorderDirectory(_PlayerHandle, recDir);

            if (NTBaseCodeDefine.NT_ERC_OK != ret)
            {
                NTSmartPlayerSDK.NT_SP_Close(_PlayerHandle);
                RemoveHandle();
                _PlayerHandle = IntPtr.Zero;

                throw new Exception("设置录像目录失败，请确保目录存在且是英文目录");
            }

            uint rec_max_file_size = (uint)_Config.LocalRecorder.SingleRecordMaxFileSize;
            rec_max_file_size *= 1024;
            NTSmartPlayerSDK.NT_SP_SetRecorderFileMaxSize(_PlayerHandle, rec_max_file_size);

            NT_SP_RecorderFileNameRuler rec_name_ruler = new NT_SP_RecorderFileNameRuler();

            string rec_file_name_prefix_ = _Config.LocalRecorder.FileNamePrefix;
            rec_name_ruler.file_name_prefix_ = rec_file_name_prefix_.ToString();

            rec_name_ruler.append_date_ = _Config.LocalRecorder.IsAppendDate ? 1 : 0;

            rec_name_ruler.append_time_ = _Config.LocalRecorder.IsAppendTime ? 1 : 0;

            NTSmartPlayerSDK.NT_SP_SetRecorderFileNameRuler(_PlayerHandle, ref rec_name_ruler);

            if (NTBaseCodeDefine.NT_ERC_OK != NTSmartPlayerSDK.NT_SP_StartRecorder(_PlayerHandle))
            {
                NTSmartPlayerSDK.NT_SP_Close(_PlayerHandle);
                RemoveHandle();
                _PlayerHandle = IntPtr.Zero;
                _IsRecording = false;

                throw new Exception("调用录像接口失败!");
            }

            _IsRecording = true;
        }

        bool _IsRecording { get; set; }

        public void StopRecord()
        {
            if (_IsRecording)
            {
                NTSmartPlayerSDK.NT_SP_StopRecorder(_PlayerHandle);
                _IsRecording = false;

                RemoveHandle();
                _PlayerHandle = IntPtr.Zero;

                WriteStatusLog();
            }

            Thread.Sleep(1000);
        }
    }
}
